<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <title>本地开发 - 为企业级框架和应用而生</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/zh-cn/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/zh-cn/intro/" alt="指南">指南</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/zh-cn/tutorials/index.html" alt="教程">教程</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="插件">插件</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="发布日志">发布日志</a></li>
      
      
        <li class="translations">
          <a class="nav-link">Translations</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/core/development.html" >English</a></li><li><a id="zh-cn" href="/zh-cn/core/development.html" style="color: #22ab28">中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1>本地开发</h1>
    <p>为了提升研发体验，我们提供了便捷的方式在本地进行开发、调试、单元测试等。</p>
<p>在这里我们需要使用到 <a href="https://github.com/eggjs/egg-bin" target="_blank" rel="noopener">egg-bin</a> 模块（只在本地开发和单元测试使用，如果线上请参考 <a href="./deployment.html">应用部署</a>）。</p>
<p>首先，我们需要把 <code>egg-bin</code> 模块作为 <code>devDependencies</code> 引入：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm i egg-bin --save-dev</span><br></pre></td></tr></table></figure>
<h2 id="启动应用"><a class="markdown-anchor" href="#启动应用">#</a> 启动应用</h2>
<p>本地启动应用进行开发活动，当我们修改代码并保存后，应用会自动重启实时生效。</p>
<h3 id="添加命令"><a class="markdown-anchor" href="#添加命令">#</a> 添加命令</h3>
<p>添加 <code>npm scripts</code> 到 <code>package.json</code>：</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"dev"</span>: <span class="string">"egg-bin dev"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样我们就可以通过 <code>npm run dev</code> 命令启动应用。</p>
<h3 id="环境配置"><a class="markdown-anchor" href="#环境配置">#</a> 环境配置</h3>
<p>本地启动的应用是以 <code>env: local</code> 启动的，读取的配置也是 <code>config.default.js</code> 和 <code>config.local.js</code> 合并的结果。</p>
<h3 id="指定端口"><a class="markdown-anchor" href="#指定端口">#</a> 指定端口</h3>
<p>本地启动应用默认监听 7001 端口，可指定其他端口，例如：</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"dev"</span>: <span class="string">"egg-bin dev --port 7001"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="单元测试"><a class="markdown-anchor" href="#单元测试">#</a> 单元测试</h2>
<p>这里主要讲解工具部分的使用，更多关于单元测试的内容请参考<a href="./unittest.html">这里</a>。</p>
<h3 id="添加命令-2"><a class="markdown-anchor" href="#添加命令-2">#</a> 添加命令</h3>
<p>添加 <code>npm scripts</code> 到 <code>package.json</code>：</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"test"</span>: <span class="string">"egg-bin test"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样我们就可以通过 <code>npm test</code> 命令运行单元测试。</p>
<h3 id="环境配置-2"><a class="markdown-anchor" href="#环境配置-2">#</a> 环境配置</h3>
<p>测试用例执行时，应用是以 <code>env: unittest</code> 启动的，读取的配置也是 <code>config.default.js</code> 和 <code>config.unittest.js</code> 合并的结果。</p>
<h3 id="运行特定用例文件"><a class="markdown-anchor" href="#运行特定用例文件">#</a> 运行特定用例文件</h3>
<p>运行 <code>npm test</code> 时会自动执行 test 目录下的以 <code>.test.js</code> 结尾的文件（默认 <a href="https://www.npmjs.com/package/glob" target="_blank" rel="noopener">glob</a> 匹配规则 <code>test/**/*.test.js</code> ）。</p>
<p>我们在编写用例时往往想单独执行正在编写的用例，可以通过以下方式指定特定用例文件：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ TESTS=<span class="built_in">test</span>/x.test.js npm <span class="built_in">test</span></span><br></pre></td></tr></table></figure>
<p>支持 <a href="https://www.npmjs.com/package/glob" target="_blank" rel="noopener">glob</a> 规则。</p>
<h3 id="指定-reporter"><a class="markdown-anchor" href="#指定-reporter">#</a> 指定 reporter</h3>
<p>Mocha 支持多种形式的 reporter，默认使用 <code>spec</code> reporter。</p>
<p>可以手动设置 <code>TEST_REPORTER</code> 环境变量来指定 reporter，例如使用 <code>dot</code>：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ TEST_REPORTER=dot npm <span class="built_in">test</span></span><br></pre></td></tr></table></figure>
<p><img src="https://cloud.githubusercontent.com/assets/156269/21849809/a6fe6df8-d842-11e6-8507-20da63bc8b62.png" alt="image"></p>
<h3 id="指定用例超时时间"><a class="markdown-anchor" href="#指定用例超时时间">#</a> 指定用例超时时间</h3>
<p>默认执行超时时间为 30 秒。我们也可以手动指定超时时间（单位毫秒），例如设置为 5 秒：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ TEST_TIMEOUT=5000 npm <span class="built_in">test</span></span><br></pre></td></tr></table></figure>
<h3 id="通过-argv-方式传参"><a class="markdown-anchor" href="#通过-argv-方式传参">#</a> 通过 argv 方式传参</h3>
<p><code>egg-bin test</code> 除了环境变量方式，也支持直接传参，支持 mocha 的所有参数，参见：<a href="https://mochajs.org/#usage" target="_blank" rel="noopener">mocha usage</a> 。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ <span class="comment"># npm 传递参数需额外加一个 `--`，参见 https://docs.npmjs.com/cli/run-script</span></span><br><span class="line">$ npm <span class="built_in">test</span> -- --<span class="built_in">help</span></span><br><span class="line">$</span><br><span class="line">$ <span class="comment"># 等同于 `TESTS=test/**/test.js npm test`，受限于 bash，最好加上双引号</span></span><br><span class="line">$ npm <span class="built_in">test</span> <span class="string">"test/**/test.js"</span></span><br><span class="line">$</span><br><span class="line">$ <span class="comment"># 等同于 `TEST_REPORTER=dot npm test`</span></span><br><span class="line">$ npm <span class="built_in">test</span> -- --reporter=dot</span><br><span class="line">$</span><br><span class="line">$ <span class="comment"># 支持 mocha 的参数，如 grep / require 等</span></span><br><span class="line">$ npm <span class="built_in">test</span> -- -t 30000 --grep=<span class="string">"should GET"</span></span><br></pre></td></tr></table></figure>
<h2 id="代码覆盖率"><a class="markdown-anchor" href="#代码覆盖率">#</a> 代码覆盖率</h2>
<p>egg-bin 已经内置了 <a href="https://github.com/istanbuljs/nyc" target="_blank" rel="noopener">nyc</a> 来支持单元测试自动生成代码覆盖率报告。</p>
<p>添加 <code>npm scripts</code> 到 <code>package.json</code>：</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"cov"</span>: <span class="string">"egg-bin cov"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样我们就可以通过 <code>npm run cov</code> 命令运行单元测试覆盖率。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ egg-bin cov</span><br><span class="line"></span><br><span class="line">  <span class="built_in">test</span>/controller/home.test.js</span><br><span class="line">    GET /</span><br><span class="line">      ✓ should status 200 and get the body</span><br><span class="line">    POST /post</span><br><span class="line">      ✓ should status 200 and get the request body</span><br><span class="line"></span><br><span class="line">  ...</span><br><span class="line"></span><br><span class="line">  16 passing (1s)</span><br><span class="line"></span><br><span class="line">=============================== Coverage summary ===============================</span><br><span class="line">Statements   : 100% ( 41/41 )</span><br><span class="line">Branches     : 87.5% ( 7/8 )</span><br><span class="line">Functions    : 100% ( 10/10 )</span><br><span class="line">Lines        : 100% ( 41/41 )</span><br><span class="line">================================================================================</span><br></pre></td></tr></table></figure>
<p>还可以通过 <code>open coverage/lcov-report/index.html</code> 打开完整的 HTML 覆盖率报告。</p>
<p><img src="https://cloud.githubusercontent.com/assets/156269/21845201/a9a85ab6-d82c-11e6-8c24-5e85f352be4a.png" alt="image"></p>
<h3 id="环境配置-3"><a class="markdown-anchor" href="#环境配置-3">#</a> 环境配置</h3>
<p>和 <code>test</code> 命令一样，<code>cov</code> 命令执行时，应用也是以 <code>env: unittest</code> 启动的，读取的配置也是 <code>config.default.js</code> 和 <code>config.unittest.js</code> 合并的结果。</p>
<h3 id="忽略指定文件"><a class="markdown-anchor" href="#忽略指定文件">#</a> 忽略指定文件</h3>
<p>对于某些不需要跑测试覆盖率的文件，可以通过 <code>COV_EXCLUDES</code> 环境变量指定：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ COV_EXCLUDES=app/plugins/c* npm run cov</span><br><span class="line">$ <span class="comment"># 或者传参方式</span></span><br><span class="line">$ npm run cov -- --x=app/plugins/c*</span><br></pre></td></tr></table></figure>
<h2 id="调试"><a class="markdown-anchor" href="#调试">#</a> 调试</h2>
<h3 id="日志输出"><a class="markdown-anchor" href="#日志输出">#</a> 日志输出</h3>
<h3 id="使用-logger-模块"><a class="markdown-anchor" href="#使用-logger-模块">#</a> 使用 logger 模块</h3>
<p>框架内置了<a href="./logger.html">日志</a> 功能，使用 <code>logger.debug()</code> 输出调试信息，<strong>推荐在应用代码中使用它。</strong></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// controller</span></span><br><span class="line"><span class="keyword">this</span>.logger.debug(<span class="string">'current user: %j'</span>, <span class="keyword">this</span>.user);</span><br><span class="line"></span><br><span class="line"><span class="comment">// service</span></span><br><span class="line"><span class="keyword">this</span>.ctx.logger.debug(<span class="string">'debug info from service'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/init.js</span></span><br><span class="line">app.logger.debug(<span class="string">'app init'</span>);</span><br></pre></td></tr></table></figure>
<p>通过 <code>config.logger.level</code> 来配置打印到文件的日志级别，通过 <code>config.logger.consoleLevel</code> 配置打印到终端的日志级别。</p>
<h3 id="使用-debug-模块"><a class="markdown-anchor" href="#使用-debug-模块">#</a> 使用 debug 模块</h3>
<p><a href="https://www.npmjs.com/package/debug" target="_blank" rel="noopener">debug</a> 模块是 Node.js 社区广泛使用的 debug 工具，很多模块都使用它模块打印调试信息，Egg 社区也广泛采用这一机制打印 debug 信息，<strong>推荐在框架和插件开发中使用它。</strong></p>
<p>我们可以通过 <code>DEBUG</code> 环境变量选择开启指定的调试代码，方便观测执行过程。</p>
<p>（调试模块和日志模块不要混淆，而且日志模块也有很多功能，这里所说的日志都是调试信息。）</p>
<p>开启所有模块的日志：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ DEBUG=* npm run dev</span><br></pre></td></tr></table></figure>
<p>开启指定模块的日志：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ DEBUG=egg* npm run dev</span><br></pre></td></tr></table></figure>
<p>单元测试也可以用 <code>DEBUG=* npm test</code> 来查看测试用例运行的详细日志。</p>
<h3 id="使用-egg-bin-调试"><a class="markdown-anchor" href="#使用-egg-bin-调试">#</a> 使用 egg-bin 调试</h3>
<h4 id="添加命令-3"><a class="markdown-anchor" href="#添加命令-3">#</a> 添加命令</h4>
<p>添加 <code>npm scripts</code> 到 <code>package.json</code>：</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"debug"</span>: <span class="string">"egg-bin debug"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样我们就可以通过 <code>npm run debug</code> 命令来断点调试应用。</p>
<p><code>egg-bin</code> 会智能选择调试协议，在 7.x 之后版本使用 <a href="https://chromedevtools.github.io/debugger-protocol-viewer/v8" target="_blank" rel="noopener">Inspector Protocol</a> 协议，低版本使用 <a href="https://github.com/buggerjs/bugger-v8-client/blob/master/PROTOCOL.md" target="_blank" rel="noopener">Legacy Protocol</a>。</p>
<p>同时也支持自定义调试参数：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ egg-bin debug --proxy=9999 --inpsect=9229 --inspect-brk</span><br></pre></td></tr></table></figure>
<ul>
<li><code>master</code> 调试端口为 9229 或 5858（旧协议）</li>
<li><code>agent</code> 调试端口固定为 5800</li>
<li><code>worker</code> 调试端口为 <code>master</code> 调试端口递增。</li>
<li>开发阶段 worker 在代码修改后会热重启，导致调试端口会自增，故 <code>egg-bin</code> 启动了代理服务，用户可以直接 attach 9999 端口即可，无需担心重启问题。</li>
</ul>
<h4 id="环境配置-4"><a class="markdown-anchor" href="#环境配置-4">#</a> 环境配置</h4>
<p>执行 <code>debug</code> 命令时，应用也是以 <code>env: local</code> 启动的，读取的配置是 <code>config.default.js</code> 和 <code>config.local.js</code> 合并的结果。</p>
<h4 id="使用-devtools-进行调试"><a class="markdown-anchor" href="#使用-devtools-进行调试">#</a> 使用 <a href="https://developer.chrome.com/devtools" target="_blank" rel="noopener">DevTools</a> 进行调试</h4>
<p>最新的 DevTools 只支持 <a href="https://chromedevtools.github.io/debugger-protocol-viewer/v8" target="_blank" rel="noopener">Inspector Protocol</a> 协议，故你需要使用 Node.js 7.x+ 的版本方能使用。</p>
<p>执行 <code>npm run debug</code> 启动：</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜  showcase git:(master) ✗ npm run debug</span><br><span class="line"></span><br><span class="line">&gt; showcase@1.0.0 debug /Users/tz/Workspaces/eggjs/<span class="built_in">test</span>/showcase</span><br><span class="line">&gt; egg-bin debug</span><br><span class="line"></span><br><span class="line">Debugger listening on ws://127.0.0.1:9229/f8258ca6-d5ac-467d-bbb1-03f59bcce85b</span><br><span class="line">For <span class="built_in">help</span> see https://nodejs.org/en/docs/inspector</span><br><span class="line">2017-09-14 16:01:35,990 INFO 39940 [master] egg version 1.8.0</span><br><span class="line">Debugger listening on ws://127.0.0.1:5800/bfe1bf6a-2be5-4568-ac7d-69935e0867fa</span><br><span class="line">For <span class="built_in">help</span> see https://nodejs.org/en/docs/inspector</span><br><span class="line">2017-09-14 16:01:36,432 INFO 39940 [master] agent_worker<span class="comment">#1:39941 started (434ms)</span></span><br><span class="line">Debugger listening on ws://127.0.0.1:9230/2fcf4208-4571-4968-9da0-0863ab9f98ae</span><br><span class="line">For <span class="built_in">help</span> see https://nodejs.org/en/docs/inspector</span><br><span class="line">9230 opened</span><br><span class="line">Debug Proxy online, now you could attach to 9999 without worry about reload.</span><br><span class="line">DevTools → chrome-devtools://devtools/bundled/inspector.html?experiments=<span class="literal">true</span>&amp;v8only=<span class="literal">true</span>&amp;ws=127.0.0.1:9999/__ws_proxy__</span><br></pre></td></tr></table></figure>
<p>然后选择以下一种方式即可：</p>
<ul>
<li>直接访问控制台最后输出的 <code>DevTools</code> 地址，该地址是代理后的 worker，无需担心重启问题。</li>
<li>访问 <code>chrome://inspect</code>，配置对应的端口，然后点击 <code>Open dedicated DevTools for Node</code> 即可打开调试控制台。</li>
</ul>
<p><img src="https://user-images.githubusercontent.com/227713/30419047-a54ac592-9967-11e7-8a05-5dbb82088487.png" alt="DevTools"></p>
<h4 id="使用-webstorm-进行调试"><a class="markdown-anchor" href="#使用-webstorm-进行调试">#</a> 使用 WebStorm 进行调试</h4>
<p><code>egg-bin</code> 会自动读取 WebStorm 调试模式下设置的环境变量 <code>$NODE_DEBUG_OPTION</code>。</p>
<p>使用 WebStorm 的 npm 调试启动即可：</p>
<p><img src="https://user-images.githubusercontent.com/227713/30423086-5dd32ac6-9974-11e7-840f-904e49a97694.png" alt="WebStorm"></p>
<h4 id="使用-vscode-进行调试"><a class="markdown-anchor" href="#使用-vscode-进行调试">#</a> 使用 <a href="https://code.visualstudio.com" target="_blank" rel="noopener">VSCode</a> 进行调试</h4>
<p>我们提供了一个 <a href="https://github.com/eggjs/vscode-eggjs" target="_blank" rel="noopener">vscode-eggjs</a> 扩展。</p>
<p><img src="https://user-images.githubusercontent.com/227713/35954428-7f8768ee-0cc4-11e8-90b2-67e623594fa1.png" alt="VSCode"></p>
<p>如图，会自动生成配置文件 <code>.vscode/launch.json</code> 如下，然后 F5 一键启动即可。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// .vscode/launch.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"version"</span>: <span class="string">"0.2.0"</span>,</span><br><span class="line">  <span class="string">"configurations"</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">"name"</span>: <span class="string">"Launch Egg"</span>,</span><br><span class="line">      <span class="string">"type"</span>: <span class="string">"node"</span>,</span><br><span class="line">      <span class="string">"request"</span>: <span class="string">"launch"</span>,</span><br><span class="line">      <span class="string">"cwd"</span>: <span class="string">"$&#123;workspaceRoot&#125;"</span>,</span><br><span class="line">      <span class="string">"runtimeExecutable"</span>: <span class="string">"npm"</span>,</span><br><span class="line">      <span class="string">"windows"</span>: &#123; <span class="string">"runtimeExecutable"</span>: <span class="string">"npm.cmd"</span> &#125;,</span><br><span class="line">      <span class="string">"runtimeArgs"</span>: [ <span class="string">"run"</span>, <span class="string">"debug"</span>, <span class="string">"--"</span>, <span class="string">"--inspect-brk"</span> ],</span><br><span class="line">      <span class="string">"console"</span>: <span class="string">"integratedTerminal"</span>,</span><br><span class="line">      <span class="string">"protocol"</span>: <span class="string">"auto"</span>,</span><br><span class="line">      <span class="string">"restart"</span>: <span class="literal">true</span>,</span><br><span class="line">      <span class="string">"port"</span>: <span class="number">9229</span>,</span><br><span class="line">      <span class="string">"autoAttachChildProcesses"</span>: <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>更多 VSCode Debug 用法可以参见文档: <a href="https://code.visualstudio.com/docs/nodejs/nodejs-debugging" target="_blank" rel="noopener">Node.js Debugging in VS Code</a></p>
<h2 id="更多"><a class="markdown-anchor" href="#更多">#</a> 更多</h2>
<p>如果想了解更多本地开发相关的内容，例如为你的团队定制一个本地开发工具，请参考 <a href="https://github.com/eggjs/egg-bin" target="_blank" rel="noopener">egg-bin</a>。</p>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/zh-cn/intro/" alt="指南">指南</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/zh-cn/tutorials/index.html" alt="教程">教程</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="插件">插件</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="发布日志">发布日志</a></li>
      
      
        <li class="translations">
          <a class="nav-link">Translations</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/core/development.html" >English</a></li><li><a id="zh-cn" href="/zh-cn/core/development.html" style="color: #22ab28">中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">新手指南<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/zh-cn/intro/index.html" class="menu-link">Egg.js 是什么?</a></li><li><a href="/zh-cn/intro/egg-and-koa.html" class="menu-link">Egg.js 和 Koa</a></li><li><a href="/zh-cn/intro/quickstart.html" class="menu-link">快速入门</a></li><li><a href="/zh-cn/tutorials/progressive.html" class="menu-link">渐进式开发</a></li><li><a href="/zh-cn/migration.html" class="menu-link">2.x 升级指南</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">基础功能<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/zh-cn/basics/structure.html" class="menu-link">目录结构</a></li><li><a href="/zh-cn/basics/objects.html" class="menu-link">内置对象</a></li><li><a href="/zh-cn/basics/env.html" class="menu-link">运行环境</a></li><li><a href="/zh-cn/basics/config.html" class="menu-link">配置</a></li><li><a href="/zh-cn/basics/middleware.html" class="menu-link">中间件</a></li><li><a href="/zh-cn/basics/router.html" class="menu-link">Router</a></li><li><a href="/zh-cn/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/zh-cn/basics/service.html" class="menu-link">Service</a></li><li><a href="/zh-cn/basics/plugin.html" class="menu-link">插件</a></li><li><a href="/zh-cn/basics/schedule.html" class="menu-link">定时任务</a></li><li><a href="/zh-cn/basics/extend.html" class="menu-link">框架扩展</a></li><li><a href="/zh-cn/basics/app-start.html" class="menu-link">启动自定义</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">核心功能<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/zh-cn/core/development.html" class="menu-link">本地开发</a></li><li><a href="/zh-cn/core/unittest.html" class="menu-link">单元测试</a></li><li><a href="/zh-cn/core/deployment.html" class="menu-link">应用部署</a></li><li><a href="/zh-cn/core/logger.html" class="menu-link">日志</a></li><li><a href="/zh-cn/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/zh-cn/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/zh-cn/core/cluster-and-ipc.html" class="menu-link">多进程模型和进程间通讯</a></li><li><a href="/zh-cn/core/view.html" class="menu-link">模板渲染</a></li><li><a href="/zh-cn/core/error-handling.html" class="menu-link">异常处理</a></li><li><a href="/zh-cn/core/security.html" class="menu-link">安全</a></li><li><a href="/zh-cn/core/i18n.html" class="menu-link">国际化</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">教程<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/zh-cn/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/zh-cn/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/zh-cn/tutorials/passport.html" class="menu-link">Passport 鉴权</a></li><li><a href="/zh-cn/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/zh-cn/tutorials/assets.html" class="menu-link">静态资源</a></li><li><a href="/zh-cn/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">进阶<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/zh-cn/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/zh-cn/advanced/plugin.html" class="menu-link">插件开发</a></li><li><a href="/zh-cn/advanced/framework.html" class="menu-link">框架开发</a></li><li><a href="/zh-cn/advanced/cluster-client.html" class="menu-link">多进程研发模式增强</a></li><li><a href="/zh-cn/advanced/view-plugin.html" class="menu-link">模板插件开发规范</a></li><li><a href="/zh-cn/style-guide.html" class="menu-link">代码风格指南</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">社区<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/zh-cn/plugins/" class="menu-link">内置插件列表</a></li><li><a href="/zh-cn/contributing.html" class="menu-link">如何贡献</a></li><li><a href="/zh-cn/resource.html" class="menu-link">资源</a></li><li><a href="/zh-cn/faq.html" class="menu-link">常见问题</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
